Ionia Design system

A visual system for an instrument, not an admin panel.

Ionia exists to make a compliance judgment defensible. Everything here follows from that: time is the primary quantity, provenance travels with every number, and no state that carries contractual meaning relies on hue alone. Radix Colors and Radix Icons supply the raw material; the taxonomy on this page is what turns them into a product.

{{ p.tag }} {{ p.title }} {{ p.body }}

Colour

Radix cyan as accent, Radix slate as neutral. Both themes are authored from the start; dark swaps values, never classes. The Metronic orange is retired — an orange accent competes with the amber that carries at-risk meaning, and in a product where colour is compliance evidence that collision is not affordable.

{{ r.name }} {{ r.note }}
{{ s.n }}
{{ g.name }} {{ g.note }}
{{ i.token }} {{ i.use }}

Typography

Archivo for interface, JetBrains Mono for identifiers, timestamps and durations. Seven steps, no more. Wired through font CSS variables, not a literal family string.

{{ t.sample }} {{ t.spec }}
Tabular figures — required {{ tickA }} Every countdown, timestamp and duration uses font-variant-numeric: tabular-nums. Digits hold their column, so a ticking clock does not shiver.
Proportional figures — never for time {{ tickA }} The same value in the interface face with proportional digits. Watch the seconds column drift. This is the current defect in the countdown cells.

Space, radius, elevation, motion

Spacing · 4px base
{{ sp.n }}
Radius · four values
{{ rd.label }}

3px on severity tags and micro-badges, 6px on controls, 10–12px on surfaces, pill on status chips. The shape difference between a 3px severity tag and a pill status chip is deliberate — see Taxonomy.

Elevation · borders first
level 0
level 1
level 2

Surfaces are separated by a 1px border and a background step. Shadow is reserved for things that float: popovers at level 1, modals and drawers at level 2. No ad-hoc shadow on cards.

Motion · comprehension only
100ms
Hover, focus, colour change
160ms
Overlay in, drawer, row state change
240ms
Threshold crossing, toast arrival
ease
cubic-bezier(.2,.7,.3,1)

No entrance animation on static content. No per-row or per-cell delay chains. Data-available to fully-settled stays under 300ms. prefers-reduced-motion removes animation rather than shortening it.

Density · persists per user
TKT-4790Comfortable · 12px
TKT-4790Compact · 7px

Compact also drops the secondary feature line under each subject, taking a 25-row page from six screens to three.

Icons · Radix Icons, one system

15×15 geometric grid at 16px nominal. Retires the four Keenicons webfont files, the hand-written inline SVGs, and Lucide. One system, one weight, one metric.

Status and severity taxonomy

One canonical mapping, used by the list, the detail header, the timeline, notifications and the report. Status is stage; severity is impact. They are different axes, so they get different shapes.

Status · pill, dot, hue advances along the pipeline
{{ s.label }} {{ s.token }} {{ s.meaning }}
“On Assessment” is removed. The backend state machine cannot produce it, so offering it as a filter guaranteed an empty result. Eight statuses, all reachable.
Severity · square tag, bar count, monospace
{{ sv.label }} {{ sv.token }} {{ sv.meaning }}
“Critical” is removed. It existed in one colour map, not in the schema default, not in the dashboard filter, and not in the backend aggregation. Three severities, all real.
Why the shapes differ
TKT-4776 High Closed

A High-severity ticket that is Closed is a normal, common object. Side by side, the sharp monospace tag reads as impact and the soft pill reads as stage — before any colour is processed. Hue alone would collapse them into one undifferentiated smear of chips.

The time language

Ionia's whole reason to exist. Four provenance sigils, four clock bands, one gauge, and a rule that red is earned rather than default.

Provenance sigils · every SLA number carries one
{{ sg.glyph }} {{ sg.name }} {{ sg.token }} {{ sg.body }}
Clock bands · magnitude is legible
{{ b.text }}
{{ b.note }}
Monochrome and colour-vision proof
In colour
Desaturated
solidhatchedsolid + ▲dashed

Fill pattern carries the state when hue cannot: solid for inside, diagonal hatch for at risk, solid plus a overrun marker for breached, dashed for no SLA. Every band also states its condition in words.

Red is earned, never default

The current response cell renders red whether four minutes or four days remain. That teaches operators to ignore red, which is the worst outcome a compliance interface can produce. In this system red appears only past the obligation, and its magnitude is stated in the same breath.

Components

Each with its full state matrix. Every Metronic class the current build leans on has a replacement here before the vendored bundle is deleted.

Button — variant × state
{{ br.name }} {{ bi.label }}

A real type attribute, not a prop that shadows it. Loading keeps the label so the button does not change width mid-request. Disabled always carries a reason through title or adjacent text — never a dead button with no explanation.

Field — label, hint, error, required
Hint text sits under the control.
{{ fieldCMessage }}

Every control has an id, and its label's for points at that id — not at the label's own text, which is the current mistake.

Skeleton, empty, error
No tickets yet Empty states name the thing that is missing and offer the action that fills it.
Request failed Says what failed, whether anything was lost, and offers retry.

Skeletons match the shape they replace. A filter change updates in place. The full-screen spinner survives only for the initial authenticated bootstrap.

Inventory
{{ iv.group }}
  • {{ it }}

Migration sequence

Nothing is deleted before its replacement is in place. Each phase ships on its own and leaves the product working.

  1. {{ ph.n }}
    {{ ph.title }} {{ ph.tag }}

    {{ ph.body }}

Flagged for separate security review, not changed here

The access token in localStorage and the client-side role guard are real weaknesses, but they are an authorisation change with its own impact analysis. This work leaves both untouched and raises them for review rather than altering them under cover of a visual revamp.

Screens

{{ sc.name }} {{ sc.route }}